/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ru.icq.io.connections;

import icq.KAN_ICQ;
import icq.protocol.ICQConstants.UserStatus;
import ru.icq.accounts.GroupjICQ;
import ru.icq.accounts.EntryjICQ;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import org.apache.log4j.Logger;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.RosterGroup;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import ru.icq.accounts.AccountInfo;
import ru.icq.listener.AccountEvent;
import ru.icq.listener.IAccountListener;

/**
 * Все соединения, которые открыты в данный момент у пользователя.
 * Класс отслеживает соединения и переподключается во время разрыва.
 * 
 * MainTree - Объект класса Jtree; dir_tree - объект класса DefaultMutableTreeNode - узел дерева MainTree
 * MainTree.collapsePath(new TreePath(dir_tree.getPath()));
 * MainTree.expandPath(new TreePath(dir_tree.getPath()));
 * @author Носов А.В.
 */
public class ConnectionsCustom implements PacketListener {

    // Variables declaration
    private static final Logger log = Logger.getLogger(ConnectionsCustom.class);
    
    /** Идентификатор соединения */
    private String id;
    private AccountInfo accountInfo;
    
    /** Слушатель */
    private AccountEvent evt = new AccountEvent();
    
    /** XMPP соединение (jabber). */
    private XMPPConnection xmpp;
    /** ICQ соединение. */
    private KAN_ICQ icq;
    
    /** Логин. */
    private boolean isLogin = false;
    /** Статус пользователя. */
    private PresenceJICQ status = PresenceJICQ.OFF_LINE;
    
    /** Список открытых чатов xmpp. */
    private HashMap<String, Chat> xmppChats;
    // End of variables declaration

    public ConnectionsCustom(AccountInfo info) {
        this.accountInfo = info;
    }

    /**
     * Соединение с сервером.
     * @param server сервер
     * @param port порт
     * @return <b>true</b> - установленно,<br>
     * <b>false</b> - разорвано.
     */
    private boolean connection() {
        try {
            switch (accountInfo.getProtocolType()) {
                case ICQ:
                    return true;
                case JABBER:
                    ConnectionConfiguration config = 
                                new ConnectionConfiguration(accountInfo.getServer(), accountInfo.getPort());
                    xmpp = new XMPPConnection(config);
                    xmpp.connect();
                    PacketFilter filter = new MessageTypeFilter(Message.Type.chat);
                    xmpp.addPacketListener(this, filter);
                    return true;
                case VKONTAKTE:
                    log.info("Протокл vkontakte не реализован.");
                    return false;
                default:
                    return false;
            }
        } catch (XMPPException ex) {
            log.error("Ошибка в xmpp при установке соединения.", ex);
            return false;
        }
    }

    /**
     * Соединение с сервером.
     * @return <b>true</b> - установленно,<br>
     * <b>false</b> - разорвано.
     */
    private boolean isConnection() {
        switch (accountInfo.getProtocolType()) {
            case ICQ:
                if (accountInfo.getServer() == null) return false;
                return true;
            case JABBER:
                if (xmpp == null) return false;
                return true;
            case VKONTAKTE:
                log.info("Протокл vkontakte не реализован.");
                return false;
            default:
                return false;
        }
    }

    /**
     * Отсоединение от сервера.
     * @return <b>true</b> - успешно отсоединен, <br>
     * <b>false</b> - не отсоединен
     */
    private boolean disconnect() {
        switch (accountInfo.getProtocolType()) {
            case ICQ:
                if ((icq != null) && (icq.isAuthorized()))
                    icq.disconnect();
                return true;
            case JABBER:
                if ((xmpp != null) && (xmpp.isConnected()))
                    xmpp.disconnect();
                return true;
            case VKONTAKTE:
                log.info("Протокл vkontakte не реализован.");
                return false;
            default:
                return false;
        }
    }

    /**
     * Устанавливает идентификатор соединения.
     * @param id идентификатор соединения
     */
    public void setID(String id) {
        this.id = id;
    }

    /**
     * Возвращает идентификатор соединения.
     * @return идентификатор
     */
    public String getID() {
        return id;
    }

    /**
     * Соединение с сервером и вход в аккаунт.
     * @param login имя пользователя
     * @param password пароль
     * @return <b>true</b> - вход осуществлен,<br>
     * <b>false</b> - вход не осуществлен
     */
    private boolean login() {
        log.info(accountInfo.getProtocolType() + " " + 
                accountInfo.getPort() + " " + 
                accountInfo.getUid() + " " + 
                accountInfo.getPassword());
        
        try {
            switch (accountInfo.getProtocolType()) {
                case ICQ:
                    if (icq != null) {
                        disconnect();
                        icq = null;
                    }
                    icq = new KAN_ICQ(accountInfo.getUid(), accountInfo.getPassword());
                    // при установке в true в system.out будут печататься отправляемые данные
                    icq.setPrintPackets(false);
                    return true;
                    
                case JABBER:
                    if (xmpp != null) {
                        disconnect();
                        xmpp = null;
                    }
                    if (connection()) {
                        xmpp.login(accountInfo.getUid(), accountInfo.getPassword());
                    } else return false;
                    return true;
                    
                case VKONTAKTE:
                    log.info("Протокл vkontakte не реализован.");
                    return false;
                default:
                    return false;
            }
        } catch (XMPPException ex) {
            log.error("Ошибка в xmpp при отправки логина.", ex);
            return false;
        }
    }

    /**
     * Соединение открыто.
     * @return <b>true</b> - открыто,<br>
     * <b>false</b> - закрыто.
     */
    public boolean isLogin() {
        if ((xmpp != null) && (xmpp.isConnected())) return true;
        return false;
    }

    /**
     * Добаляет слушателя.
     * @param l слушатель
     */
    public void addListener(IAccountListener l) {
        evt.addMyListener(l);
    }

    /**
     * Устанавливает присутствие (Статус).
     * @param status статус
     */
    public void setPresenceJICQ(PresenceJICQ status) {
        if ((!isLogin) && (status != PresenceJICQ.OFF_LINE)) 
            if (!login()) {
                log.error("Попытка прилогиница не увенчалась успехом.");
                return;
            }
        
        switch (accountInfo.getProtocolType()) {
            case ICQ:
                switch (status) {
                    case AWAY:
                        icq.setStatus(UserStatus.STATUS_AWAY);
                        break;
                    case DO_NOT_DIDTRUB:
                        icq.setStatus(UserStatus.STATUS_DND);
                        break;
                    case FREE_FOR_CHAT:
                        icq.setStatus(UserStatus.STATUS_FREE4CHAT);
                        break;
                    case INVISIBLE:
                        icq.setStatus(UserStatus.STATUS_INVISIBLE);
                        break;
                    case NOT_AVAILABLE:
                        icq.setStatus(UserStatus.STATUS_EVIL);
                        break;
                    case ON_LINE:
                        icq.setStatus(UserStatus.STATUS_ONLINE);
                        break;
                    case OFF_LINE:
                        if (disconnect()) isLogin = false;
                        break;
                }
                break;
            case JABBER:
                Presence presence = new Presence(Presence.Type.available);
                switch (status) {
                    case AWAY:
                        presence.setMode(Presence.Mode.away);
                        xmpp.sendPacket(presence);
                        break;
                    case DO_NOT_DIDTRUB:
                        presence.setMode(Presence.Mode.dnd);
                        xmpp.sendPacket(presence);
                        break;
                    case FREE_FOR_CHAT:
                        presence.setMode(Presence.Mode.chat);
                        xmpp.sendPacket(presence);
                        break;
                    case INVISIBLE:
                        log.info("Нет такого статуса");
                        break;
                    case NOT_AVAILABLE:
                        presence.setMode(Presence.Mode.xa);
                        xmpp.sendPacket(presence);
                        break;
                    case ON_LINE:
                        presence.setMode(Presence.Mode.available);
                        xmpp.sendPacket(presence);
                        break;
                    case OFF_LINE:
                        if (disconnect()) isLogin = false;
                        break;
                }
                break;
            case VKONTAKTE:
                log.info("Протокл vkontakte не реализован.");
                break;
            default:
                break;
        }
        this.status = status;
        evt.changePresence(this);
    }

    /**
     * Возвращает присутствие (Статус).
     * @return статус
     */
    public PresenceJICQ getPresenceJICQ() {
        return status;
    }

    /**
     * Возвращает список пользователей.
     * @return список пользователей.
     */
    public List<EntryjICQ> displayBuddyList() {
        Roster roster = xmpp.getRoster();
        List<EntryjICQ> root = new ArrayList<EntryjICQ>();
        
        Collection<RosterEntry> entries = roster.getEntries();
        
//        log.info("---------------  Count: " + entries.size() + " buddy(ies):");
        for (RosterEntry r : entries) {
            EntryjICQ e = new EntryjICQ(this);
            e.setAccount(id);
            e.setType(r.getType().name());
            e.setName(r.getName());
            e.setUser(r.getUser());
            
//            log.info("user:\t" + r.getUser());
//            log.info("name:\t" + r.getName());
//            log.info("status:\t" + r.getStatus());
//            log.info("type:\t" + r.getType());
            ArrayList<GroupjICQ> gs = new ArrayList<GroupjICQ>();
            Collection<RosterGroup> groups = r.getGroups();
            for (RosterGroup group : groups) {
//                log.info("group:\t" + group.getName());
                gs.add(new GroupjICQ(group.getName()));
            }
//            System.out.println("-----------------------");
            e.setGroup(gs);
            root.add(e);
        }

        return root;
    }

    /**
     * Открыть чат с пользователем.
     * @param to пользователь
     * @param listener слушатель
     */
    public void createChat(String to, MessageListener listener) {
        switch (accountInfo.getProtocolType()) {
            case ICQ:
                break;
            case JABBER:
                if (xmppChats == null)
                    xmppChats = new HashMap<String, Chat>();
                log.info("Открыть чат с " + to);
                Chat chat = xmpp.getChatManager().createChat(to, listener);
                xmppChats.put(to, chat);
                log.info("Чат открыт c " + to);
                break;
            case VKONTAKTE:
                log.info("Протокл vkontakte не реализован.");
                break;
            default:
                break;
        }
    }

    /**
     * Закрыть чат с пользователем.
     * @param to поьзователь
     */
    public void closeChat(String to) {
        switch (accountInfo.getProtocolType()) {
            case ICQ:
                break;
            case JABBER:
                xmppChats.remove(to);
                log.info("Чат закрыт c " + to);
                break;
            case VKONTAKTE:
                log.info("Протокл vkontakte не реализован.");
                break;
            default:
                break;
        }
    }

    public void sendMessage(String to, String msg) {
        try {
            switch (accountInfo.getProtocolType()) {
                case ICQ:
                    break;
                case JABBER:
                    Chat chat = xmppChats.get(to);
                    chat.sendMessage(msg);
                    break;
                case VKONTAKTE:
                    log.info("Протокл vkontakte не реализован.");
                    break;
                default:
                    break;
            }
        } catch (XMPPException ex) {
            log.error("Ошибка при передаче сообщения.", ex);
        }
    }

//    private void createChatXmpp(String to) {
//        if (xmpp == null) ui.showError("Нет соединения.");
//        
//        Chat chat = xmpp.getChatManager().createChat(to, this);
//        chats.put(to, chat);
//    }
//
//    @Override
//    public void processMessage(Chat chat, Message msg) {
//        ui.getMessage(msg);
//    }

    @Override
    public void processPacket(Packet packet) {
        
        if (packet instanceof Message) {
            Message message = (Message)packet;
            switch (message.getType()) {
                case chat:
                    evt.processChatMessage(message.getFrom(), message.getBody());
                    break;
                case error:
                    break;
                case groupchat:
                    break;
                case headline:
                    break;
                case normal:
                    break;
                default:
                    break;
            }
        } else {
            log.info("Packet: " + packet.getFrom() + "   " +packet.getPacketID());
        }
    }

}
